package cn.sourcespro.shiro.conf;

import cn.sourcespro.shiro.entity.User;
import cn.sourcespro.shiro.security.LoginType;
import cn.sourcespro.shiro.service.PermService;
import cn.sourcespro.shiro.service.RoleService;
import cn.sourcespro.shiro.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.Set;

/**
 * security
 *
 * @author zhanghaowei
 * @date 2018/7/13
 */
@Component
public class CustomRealm extends AuthorizingRealm {

    private static final Logger logger = LoggerFactory.getLogger(CustomRealm.class);

    @Autowired
    private UserService userService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private PermService permService;
    @Autowired
    private RedisSessionDAO sessionDAO;

    {
        //设置用于匹配密码的CredentialsMatcher
        HashedCredentialsMatcher hashMatcher = new HashedCredentialsMatcher();
        hashMatcher.setHashAlgorithmName(Sha256Hash.ALGORITHM_NAME);
        hashMatcher.setHashIterations(2);
        this.setCredentialsMatcher(hashMatcher);
        setName(LoginType.SESSION_USER.getType());
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        if (principals == null) {
            throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
        }

        User user = (User) super.getAvailablePrincipal(principals);
        Set<String> roles = roleService.findByUserId(user.getId());
        Set<String> perms = permService.findByUserId(user.getId());

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        logger.info("获取角色信息：{}", roles);
        logger.info("获取权限信息：{}", perms);
        info.setRoles(roles);
        info.setStringPermissions(perms);
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        String username = upToken.getUsername();

        if (username == null) {
            throw new AccountException("Null usernames are not allowed by this realm.");
        }

        User user = userService.findByUsername(username);
        if (user == null) {
            throw new UnknownAccountException("No account found for admin [" + username + "]");
        }

        //单用户登录
        //处理session
        DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) SecurityUtils.getSecurityManager();
        DefaultWebSessionManager sessionManager = (DefaultWebSessionManager) securityManager.getSessionManager();
        Session currSession = SecurityUtils.getSubject().getSession();
        //获取当前已登录的用户session列表
        Collection<Session> sessions = sessionManager.getSessionDAO().getActiveSessions();
        User temp;
        for(Session session : sessions){
            if (currSession.getId().equals(session.getId())) {
                logger.info("登录相同账户");
                continue;
            }
            //清除该用户以前登录时保存的session，强制退出
            Object attribute = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
            if (attribute == null) {
                continue;
            }

            temp = (User) ((SimplePrincipalCollection) attribute).getPrimaryPrincipal();
            if(username.equals(temp.getUsername())) {
                session.setAttribute("kickout", true);
                sessionDAO.update(session);
                //sessionManager.getSessionDAO().delete(session);
            }
        }

        Set<String> roles = roleService.findByUserId(user.getId());
        Set<String> perms = permService.findByUserId(user.getId());
        user.getRoles().addAll(roles);
        user.getPerms().addAll(perms);

        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), getName());
        if (user.getSalt() != null) {
            info.setCredentialsSalt(ByteSource.Util.bytes(user.getSalt()));
        }

        return info;
    }
}
